package com.sz.netty.service;

import com.sz.netty.server.handler.BinaryDataHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Enhanced device command manager with improved reliability and retry mechanisms
 */
@Slf4j
@Service
public class DeviceCommandManager {
    
    // Track command send attempts per device
    private static final ConcurrentHashMap<String, AtomicInteger> deviceCommandAttempts = new ConcurrentHashMap<>();
    
    // Maximum attempts per device before giving up
    private static final int MAX_DEVICE_COMMAND_ATTEMPTS = 3;
    
    /**
     * Check if a device is currently connected
     * @param deviceId Device ID
     * @return true if device is connected, false otherwise
     */
    public static boolean isDeviceConnected(String deviceId) {
        return BinaryDataHandler.isDeviceConnected(deviceId);
    }
    
    /**
     * Send remote display content to device with enhanced reliability
     * 
     * @param deviceId Device ID
     * @param displayContent 3-character display content (e.g., "901")
     * @return true if command was sent successfully, false otherwise
     */
    public static boolean sendRemoteDisplayContentReliable(String deviceId, String displayContent) {
        try {
            // Validate input
            if (displayContent == null || displayContent.length() != 3) {
                log.warn("Invalid display content for device {}: expected 3 characters, got '{}'", deviceId, displayContent);
                return false;
            }
            
            // Check if device is connected
            if (!isDeviceConnected(deviceId)) {
                log.warn("Device {} is not connected, cannot send command '{}'", deviceId, displayContent);
                return false;
            }
            
            // Convert string to bytes
            byte[] contentBytes = displayContent.getBytes(StandardCharsets.US_ASCII);
            
            // Get or create attempt counter for this device
            AtomicInteger attempts = deviceCommandAttempts.computeIfAbsent(deviceId, k -> new AtomicInteger(0));
            
            // Check if we've exceeded maximum attempts
            if (attempts.get() >= MAX_DEVICE_COMMAND_ATTEMPTS) {
                log.warn("Exceeded maximum command attempts ({}) for device {}, resetting counter", 
                        MAX_DEVICE_COMMAND_ATTEMPTS, deviceId);
                attempts.set(0);
            }
            
            // Increment attempt counter
            int attemptNumber = attempts.incrementAndGet();
            log.info("Sending command '{}' to device {} (attempt {}/{})", 
                    displayContent, deviceId, attemptNumber, MAX_DEVICE_COMMAND_ATTEMPTS);
            
            // Add progressive delays based on attempt number
            if (attemptNumber > 1) {
                long delayMs = 500 * (attemptNumber - 1); // 500ms, 1000ms, 1500ms, etc.
                log.debug("Adding delay of {}ms before sending command", delayMs);
                try {
                    Thread.sleep(delayMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return false;
                }
            }
            
            // For display commands, ensure the channel is ready
            if (!ensureChannelReady(deviceId)) {
                log.warn("Channel for device {} is not ready for sending display command '{}'", deviceId, displayContent);
                return false;
            }
            
            // Send command with the enhanced method
            boolean result = BinaryDataHandler.sendRemoteDisplayContent(deviceId, contentBytes);
            
            if (result) {
                log.info("Successfully sent command '{}' to device {} on attempt {}", 
                        displayContent, deviceId, attemptNumber);
                // Reset attempt counter on success
                attempts.set(0);
            } else {
                log.warn("Failed to send command '{}' to device {} on attempt {}/{}", 
                        displayContent, deviceId, attemptNumber, MAX_DEVICE_COMMAND_ATTEMPTS);
                
                // If this was the last attempt, reset counter
                if (attemptNumber >= MAX_DEVICE_COMMAND_ATTEMPTS) {
                    attempts.set(0);
                }
            }
            
            return result;
        } catch (Exception e) {
            log.error("Error sending remote display content '{}' to device {}: {}", 
                    displayContent, deviceId, e.getMessage(), e);
            // Reset attempt counter on error
            deviceCommandAttempts.computeIfPresent(deviceId, (k, v) -> {
                v.set(0);
                return v;
            });
            return false;
        }
    }
    
    /**
     * Ensure the channel is ready for sending data
     * This includes checking if the channel is active and writable, and adding a small delay
     * to ensure the device is ready to receive the command
     * 
     * @param deviceId Device ID
     * @return true if channel is ready, false otherwise
     */
    private static boolean ensureChannelReady(String deviceId) {
        try {
            // Check if device is connected
            if (!isDeviceConnected(deviceId)) {
                log.warn("Device {} is not connected", deviceId);
                return false;
            }
            
            // Add a delay to ensure device is ready to receive the command
            // This is especially important when sending multiple commands in sequence
            try {
                Thread.sleep(100); // Small delay to ensure device readiness
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
            
            return true;
        } catch (Exception e) {
            log.error("Error ensuring channel readiness for device {}: {}", deviceId, e.getMessage(), e);
            return false;
        }
    }

    /**
     * Reset command attempt counter for a device
     * 
     * @param deviceId Device ID
     */
    public static void resetCommandAttempts(String deviceId) {
        deviceCommandAttempts.computeIfPresent(deviceId, (k, v) -> {
            v.set(0);
            return v;
        });
    }
    
    /**
     * Get current command attempt count for a device
     * 
     * @param deviceId Device ID
     * @return Current attempt count
     */
    public static int getCommandAttempts(String deviceId) {
        AtomicInteger attempts = deviceCommandAttempts.get(deviceId);
        return attempts != null ? attempts.get() : 0;
    }
    
    /**
     * Get the list of connected devices
     * @return Array of connected device IDs
     */
    public static String[] getConnectedDevices() {
        return BinaryDataHandler.getConnectedDevices();
    }
}